package br.ufrj.ppgi.infovis.mtgraph;

import java.util.HashSet;
import java.util.Set;

import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleProcessor;
import org.mt4j.util.MTColor;


import br.ufrj.ppgi.infovis.mtgraph.shapes.LabeledCircle;

/**
 * @author Ewoud van Pareren
 * 
 * A simple node class for MT Tree.
 * 
 */
public class GraphNode {
	private Set<AdjacentEntry> adjacentNodes;
	
	//private boolean show;
	
	private Graph graph = null; // A reference to the global tree scene obj
	private LabeledCircle component;

	private String _id;
	private String _label;
	private Boolean _selected;
	private String _type;
	private MTColor _color;
	
	/**
	 * This constructor creates a simple node.
	 * 
	 */
	public GraphNode() {
		adjacentNodes = new HashSet<AdjacentEntry>();
		//show = true;
		this._selected = false;
		this._color = null;
		
	}
	
	public AdjacentEntry[] getAllAdjacentNodes() {
		return this.adjacentNodes.toArray( new AdjacentEntry[0]);
	}
	
	public void removeAdjacent(GraphNode node){
		AdjacentEntry found = null;
		for (AdjacentEntry e : adjacentNodes) {
			if (node.equals(e.getNode())) {
				found = e;
			}
		}
		
		if (found != null)
			adjacentNodes.remove(found);		
		
	}
	
	public void removeAdjacent(GraphEdge edge){
		AdjacentEntry found = null;
		for (AdjacentEntry e : adjacentNodes) {
			if (edge.equals(e.getEdge())) {
				found = e;
			}
			
		}
		
		if (found != null)
			adjacentNodes.remove(found);		
	}
	
	public void removeAdjacent(AdjacentEntry entry){
		adjacentNodes.remove(entry);		
	}
	
	
	public AdjacentEntry getAdjacent(GraphEdge edge){
		for (AdjacentEntry e : adjacentNodes) {
			if (edge.equals(e.getEdge())) {
				return e;
			}
			
		}
		return null;
	}
	
	public AdjacentEntry getAdjacent(GraphNode node){
		for (AdjacentEntry e : adjacentNodes) {
			if (node.equals(e.getNode())) {
				return e;
			}
			
		}
		return null;		
	}
	
	public boolean isAdjacent(AdjacentEntry entry) {
		return adjacentNodes.contains(entry);
	}

	public boolean isAdjacent(GraphNode node) {
		return getAdjacent(node) != null;
	}
	
	public boolean isAdjacent(GraphEdge edge) {
		return getAdjacent(edge) != null;
	}
	
	public void addAdjacent(AdjacentEntry entry) {
		adjacentNodes.add(entry);
	}
	
	
	/**
	 * Set the current component and, if needed, add it to the tree scene.
	 * This variant makes a default component (using TreeScene.makeDefaultComponent()).
	 * 
	 * Throws RuntimeException if there is no TreeScene to use.
	 *  
	 */
	public void setComponent() {
		if ((graph == null) || (graph.getGraphScene() == null))
			throw new RuntimeException("Cannot make component, no tree to attach.");
		
		setComponent(graph.getGraphScene().makeDefaultComponent());
		component.addGestureListener(ScaleProcessor.class, new GraphLineDragAction(this));
		component.removeAllGestureEventListeners(DragProcessor.class);
		component.addGestureListener(DragProcessor.class, new GraphLineDragAction(this));
		component.addGestureListener(RotateProcessor.class, new GraphLineDragAction(this));
		
		GraphNodeGestures gng = new GraphNodeGestures(graph.getGraphScene(), this);
		gng.addGestures();
		
		if (_color != null) {
			component.setColor(_color);
		}
		
		if (_label != null) {
			component.setLabel(_label);
		}
		
		
	}

	/**
	 * Set the current component and, if needed, add it to the tree scene.
	 * This variant takes a MTRectangle to use as component.
	 * 
	 * Throws RuntimeException if there is no TreeScene to use.
	 * 
	 * 
	 * Note that the component can be set to null, in order to remove the component without
	 * adding a new one. In that case, a default component will be generated on the next
	 * Tree update.
	 * 
	 * If this node, or its Tree, is to be detached from its TreeScene (or MTApplication),
	 * the current component should be removed by setComponent(null) and reattached/copied
	 * after the move. 
	 * 
	 * @param lbCircle the component to associate with this node
	 */
	public void setComponent(LabeledCircle lbCircle) {
		if (graph == null)
			throw new RuntimeException("Cannot set component, no Tree(Scene) to attach.");
		else if (graph.getGraphScene() == null)
			throw new RuntimeException("Cannot set component, no TreeScene to attach.");
		
		// Destroy any previous component
		if (component != null) {
			// Attempt to remove from TreeScene
			graph.getGraphScene().removeChild(component);
			component.destroy();
		}
		// Note that this will cause the scene to have 'missing nodes'
		// unless you update...
		
		// The new component thing
		component = lbCircle;
		lbCircle.setLabel(this.getLabel());
		
		if (_color != null) {
			lbCircle.setColor(_color);
		}
		
		if (graph != null) graph.invalidate();
	}
	
	/**
	 * Get the component reference.
	 * 
	 * Can give null if no component is set.
	 * 
	 * @return the MT4j component, or null if there is none
	 */
	public LabeledCircle getComponent() {
		return component;
		
	}
	
	public String getLabel() {
		return this._label;
	}
	
	public void setLabel(String text) {
		this._label = text;
	}
	
	/**
	 * This is called when adding this node's component to the TreeScene.
	 * 
	 * It is used to add event listeners (like dragging) to the component.
	 * 
	 * Subclasses that add additional listeners should override this method.
	 * 
	 * @param scene the scene to which the component is added (convenience)
	 */
	protected void setComponentListeners(GraphScene scene) {
		GraphNodeGestures gestures = new GraphNodeGestures(scene, this);
		gestures.addGestures();
		
		// Add a gesture listener for dragging, one that updates the edge lines	
		component.registerInputProcessor(new DragProcessor(scene.getMTApplication()));
		component.addGestureListener(DragProcessor.class, new GraphLineDragAction(this));
	}
	
		
	/**
	 * Get the Tree instance this node is a part of.
	 * 
	 * @return an instance of Tree, or null if partOfTree() is false
	 */
	public Graph getGraph() {
		return this.graph;
	}
	
	
	/*
	 * Some less public methods...
	 */
	
	
	public void setGraph(Graph graph) {
		this.graph = graph;
	}

	public void setId(String id) {
		this._id = id;
	}
	
	public String getId() {
		return this._id;
	}
	
	public void setSelected(Boolean select) {
		this._selected = select;
	}
	
	public Boolean getSelected() {
		return this._selected;
	}

	public String getType() {
		return _type;
	}

	public void setType(String _type) {
		this._type = _type;
	}

	public MTColor getColor() {
		return _color;
	}

	public void setColor(MTColor _color) {
		this._color = _color;
	}
}
